<?php
/**
 * Created by PhpStorm.
 * User: worry
 * Date: 2017/12/20
 * Time: 22:20
 */

namespace common\components;


use common\controllers\BaseController;
use ErrorException;
use Yii;
use yii\helpers\ArrayHelper;

class Error extends BaseController
{

    const FAILED = 'FAILED';
    const SAVE_FAILED = 'SAVE_FAILED';
    const DELETE_FAILED = 'DELETE_FAILED';
    const LOGIN_FAILED = 'LOGIN_FAILED';
    const USER_NOT_EXISTS = 'USER_NOT_EXISTS';
    const PASS_ERROR = 'PASS_ERROR';

    /**
     * 错误异常
     * @param $msg
     * @param string $code
     * @throws \Exception
     */
    public static function exception($msg, $code = 'FAILED')
    {
        $message = $msg;
        if ($code) {
            $message .= '|#|' . $code;
        }
        throw new \Exception($message);
    }

    /**
     * 异常环境公用处理
     * @param callable $func
     * @param callable $exceptionHandle
     * @param string $requestType
     * @param bool $isTransaction
     * @return mixed
     * @throws \yii\db\Exception
     */
    public static function tryCatch(callable $func, callable $exceptionHandle = null, $requestType = '', $isTransaction = false)
    {
        $transaction = null;
        if ($isTransaction) {
            $transaction = Yii::$app->db->beginTransaction();
        }
        // 兼容php5 和php7的写法
        try {
            switch (strtolower($requestType)) {
                case 'get':
                    if (Yii::$app->request->isGet) {
                        self::exception('请求错误');
                    }
                    break;
                case 'post':
                    if (Yii::$app->request->isPost) {
                        self::exception('请求错误');
                    }
                    break;
                case 'ajax':
                    if (Yii::$app->request->isAjax) {
                        self::exception('请求错误');
                    }
                    break;
            }
            $result = call_user_func($func);
            $transaction && $transaction->commit();
            return $result ?: null;
        } catch (\Throwable $e) {
            $transaction && $transaction->rollBack();
            return $exceptionHandle ? call_user_func($exceptionHandle, $e) : self::exceptionHandle($e);
        } catch (\Exception $e) {
            $transaction && $transaction->rollBack();
            return $exceptionHandle ? call_user_func($exceptionHandle, $e) : self::exceptionHandle($e);
        }
    }

    /**
     * 异常处理
     * @param \Exception $e meesage 以|#|分隔文字信息和错误代码
     * @return array
     * @throws \Exception
     */
    public static function exceptionHandle($e)
    {
        if (Yii::$app->request->isAjax) {
            $status = 'FAILED';
            $message = $e->getMessage();
            $file = $e->getFile();
            $line = $e->getLine();
            if (strpos($message, '|#|') !== false) {
                $tmp = explode('|#|', $message);
                $message = $tmp[0];
                count($tmp) > 1 && $status = $tmp[1];
            }
            $trace = self::handleTrace($e->getTrace());
            return array_merge(parent::ajaxReturn($message, $status), ['trace' => $trace, 'file' => $file, 'line' => $line]);
        }
        throw $e;
    }

    /**
     * 处理异常trace防止json_encode出现递归错误
     * @param $trace
     * @return array
     */
    public static function handleTrace(array $trace)
    {
        $result = [];
        foreach ($trace as $item) {
            $tmp = [
                'file' => ArrayHelper::getValue($item, 'file'),
                'line' => ArrayHelper::getValue($item, 'line'),
                'function' => ArrayHelper::getValue($item, 'function'),
                'class' => ArrayHelper::getValue($item, 'class'),
                'type' => ArrayHelper::getValue($item, 'type'),
                'args' => [],
            ];
            //参数处理
            if (isset($item['args']) && is_array($item['args'])) {
                foreach ($item['args'] as $subItem) {
                    switch (gettype($subItem)) {
                        case 'boolean':
                        case 'integer':
                        case 'double':
                        case 'string':
                        case 'NULL':
                            $tmp['args'][] = $subItem;
                            break;
                        case 'array':
                            $tmp['args'][] = 'array';
                            break;
                        case 'object':
                            $tmp['args'][] = 'Class ' . get_class($subItem);
                            break;
                        case 'resource':
                            $tmp['args'][] = 'Resouce ' . get_resource_type($subItem);
                            break;
                        default:
                            $tmp['args'][] = 'unknown type';
                            break;
                    }
                }
            }
            $result[] = $tmp;
        }
        return $result;
    }

    /**
     * 检查错误
     * @param \Yii\base\Model $Model
     * @throws \Exception
     */
    public static function checkErr($Model)
    {
        if ($Model->validate() === false) {
            $firstErrors = $Model->getFirstErrors();
            $firstError = array_shift($firstErrors);
            self::exception($firstError . '<model>' . get_class($Model) . '</model>', 'MODEL_VALIDATE_ERROR');
        }
    }

}